/*
 * Copyright 2008 jarics Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package com.jarics.gwt.client.composites;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.SourcesTableEvents;
import com.google.gwt.user.client.ui.TableListener;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;

/**
 * 
 * Some styles that are usable for that grid:
 * 
 * .mobi-Grid { color: black; margin: 1px; padding: 2px; border: thin ridge
 * gainsboro; font-size: 14px; } .mobi-Grid .header { color: Black; font-size:
 * 14px; background-color: powderblue; } .mobi-Grid .selected { color: #ffffff;
 * background-color: #0099cc; } .mobi-Grid .over { color: #ffffff;
 * background-color: powderblue; } </PRE>
 * 
 * <P>
 * You can take advantage of the default style by adding the following to the
 * head of your HTML page.
 * 
 * <!-- Begin Jarics library files --> <link rel="stylesheet" type="text/css"
 * href="style/jaricsGrid.css"/> <!-- End Jarics library files -->
 * 
 * <P>
 * This style sheet also has additional styles which you can use by adding the
 * style name to the widget. You can use either one of these, or use both
 * combined.
 * 
 * 
 * 
 * Wraps a GridableDefaultClass object in a Grid. All the methods are also here.
 * 
 * @author eaudet
 * 
 */
public class JaricsGrid extends Composite {

	private List gridables = new ArrayList();
	private ColumnConfigIf[] columnConfigs;
	FlexTable headerGrid;
	FlexTable grid;
	private int selectedIdx = 0;
	private ScrollPanel panel;
	private boolean selectable = true;
	
	public JaricsGrid(boolean selectable) {
		this();
		this.selectable = selectable;
	}
	
	public JaricsGrid() {
		
		VerticalPanel mainPanel = new VerticalPanel();
		initWidget(mainPanel);

		//instantiate the header grid
		headerGrid = new FlexTable();
		mainPanel.add(headerGrid);
//		headerGrid.setWidth("100%");
		headerGrid.setCellSpacing(2);
		headerGrid.setCellPadding(2);
		headerGrid.setStyleName("mobi-Grid .header");
		
		panel = new ScrollPanel();
		panel.setWidth("100%");
		panel.setAlwaysShowScrollBars(false);
		mainPanel.add(panel);
		panel.setStyleName("gwt-StackPanel");
		
		//instantiate the data grid
		grid = new FlexTable();

		grid.addTableListener(jaricsGridListener());

		panel.add(grid);
//		grid.setWidth("100%");
		grid.setCellSpacing(2);
		grid.setCellPadding(2);
		grid.setStyleName("mobi-Grid .header");

		// default values
		columnConfigs = new ColumnConfigIf[] {
		// column ID is company which is later used in setAutoExpandColumn
		new ColumnConfigImpl("Column1", "Column1", ColumnConfigImpl.TEXT_BOX) };

		buildHeader();

	}
	
	private void formatSelectedRow() {
		if (selectable) {
			for (int i = 0; i < gridables.size(); i++) {
				if (selectedIdx == i) {
					grid.getRowFormatter().addStyleName(i, "selected");
				} else {
					grid.getRowFormatter().removeStyleName(i, "selected");
				}
			}
		}
	}

	private void buildHeader() {

		// build grid layouts
		for (int colIdx = 0; colIdx < columnConfigs.length; colIdx++) {
			addColHeader(colIdx);
		}
	}

	private void addColHeader(int colIdx) {
		ColumnConfigIf wColumnConfig = columnConfigs[colIdx];

		MobiLabel wCell = new MobiLabel(wColumnConfig.getHeader(), false, 0, null);
		wCell.setTitle(wColumnConfig.getToolTip());
		wCell.setHeight("20px");
//		wCell.setWidth("10px");
		wCell.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_LEFT);
		headerGrid.getRowFormatter().addStyleName( 0, "header");
		headerGrid.setWidget(0, colIdx, wCell);
	}

	/**
	 * Loops through each row and every columns to create cells in the table.
	 * Every cell must also update the width the header (the max best size is
	 * the final width of the column).
	 */
	public void refreshData() {

		for (int j = 0; j < headerGrid.getCellCount(0); j++) {
			MobiLabel m = (MobiLabel)headerGrid.getWidget(0, j);
			m.setWidth("0px");
		}
		
		//		
		for (int i = 0; i < grid.getRowCount(); i++) {
			for (int j = 0; j < grid.getCellCount(i); j++) {
				grid.clearCell(i, j);
			}
		}

		// fill grid with data

		if (gridables != null) {
			int rowIdx = 0;
			for (Iterator iter = gridables.iterator(); iter.hasNext();) {
				Gridable wGridableDTO = (Gridable) iter.next();
				wGridableDTO.setJaricsGrid(this);
				for (int colIdx = 0; colIdx < columnConfigs.length; colIdx++) {

					ColumnConfigImpl columnConfig = (ColumnConfigImpl) columnConfigs[colIdx];
					int wUiType = columnConfig.getUiType();

					switch (wUiType) {
					case ColumnConfigImpl.LIST_BOX: {
						addListBoxCell(rowIdx, wGridableDTO, colIdx);
						break;
					}					
					case ColumnConfigImpl.IMAGE: {
						addImageCell(rowIdx, wGridableDTO, colIdx);
						break;
					}
					case ColumnConfigImpl.TEXT_BOX: {
						addTextBoxCell(rowIdx, wGridableDTO, colIdx);
						break;
					}
					case ColumnConfigImpl.LABEL: {
						addLabelCell(rowIdx, wGridableDTO, colIdx);
						break;
					}
					case ColumnConfigImpl.CHECK_BOX: {
						addCheckBoxCell(rowIdx, wGridableDTO, colIdx);
						break;
					}
					case ColumnConfigImpl.BUTTON_BOX: {
						addButtonCell(rowIdx, wGridableDTO, colIdx);
						break;
					}	
					case ColumnConfigImpl.RICH_TEXT_AREA: {
						addRichTextAreaCell(rowIdx, wGridableDTO, colIdx);
						break;
					}
					case ColumnConfigImpl.CUSTOM: {
						addCustomCell(rowIdx, wGridableDTO, colIdx);
						break;
					}
					default: {
						addImageCell(rowIdx, wGridableDTO, colIdx);
						break;
					}
					}

				}
				++rowIdx;

			}
			
			formatSelectedRow();
		}
		headerGrid.setWidth(grid.getOffsetWidth()+"px");

	}
	
	
	/**
	 * This method handles on mouse click events related to the grid.
	 * For each GridWidget, you need to use specific methods mouseListenerHandler and focusListenerHandler
	 * @see mouseListenerHandler and focusListenerHandler
	 * @return
	 */
	private TableListener jaricsGridListener(){
		return new TableListener() {

			public void onCellClicked(SourcesTableEvents sender, int row,
					int cell) {
				selectedIdx = row;
				formatSelectedRow();				
			}
		};
	}

	private void addImageCell(int rowIdx, Gridable wGridableDTO, int colIdx) {
		ColumnConfigIf wColumnConfig = columnConfigs[colIdx];
		String wUrl = GWT.getModuleBaseURL() + wGridableDTO.getColumnData(colIdx);
		MobiImage wCell = new MobiImage(wUrl, rowIdx, wGridableDTO, grid);
		wCell.setTitle(wColumnConfig.getToolTip());
		setSize(wColumnConfig, (GridWidget)wCell, "", colIdx);
		grid.setWidget(rowIdx, colIdx, wCell);
	}
	
	private void addCheckBoxCell(int rowIdx, Gridable wGridableDTO, int colIdx) {
		ColumnConfigIf wColumnConfig = columnConfigs[colIdx];		
		MobiCheckBox wCell = new MobiCheckBox(null, true, rowIdx, wGridableDTO, grid);
		setSize(wColumnConfig, wCell, "", colIdx);
		grid.setWidget(rowIdx, colIdx, wCell);
	}	
	
	private void addCustomCell(int rowIdx, Gridable wGridableDTO, int colIdx) {
		CustomGridWidget cgw = (CustomGridWidget)wGridableDTO.getColumnData(colIdx);
		cgw.setCustomGridWidget(null, false, rowIdx, wGridableDTO, grid);
		grid.setWidget(rowIdx, colIdx, (Widget) wGridableDTO.getColumnData(colIdx));
	}	

	/*
	 * Sets cells sizes
	 */
	private void setSize(ColumnConfigIf wColumnConfig,
			GridWidget wCell, String pValue, int pColIdx) {

		int numberOfPixelPerCharacters = 10; //10 pixel per chars
		int wHeaderSizeInPixels = wColumnConfig.getHeader().length() * numberOfPixelPerCharacters;
		int wValueSizeInPixels = pValue.length() * numberOfPixelPerCharacters;
		
		// if no size is available in the column config, simply determine the best fit
		MobiLabel wHeader = (MobiLabel) headerGrid.getWidget(0, pColIdx);
		if ( wColumnConfig.getWidth() > 0 && wColumnConfig.getHeight() > 0) {
			wCell.setWidth(Integer.toString(wColumnConfig.getWidth())+ "px");
			wCell.setHeight(Integer.toString(wColumnConfig.getHeight())+ "px");
			if (wHeader.getOffsetWidth() < wColumnConfig.getWidth())
				wHeader.setWidth(Integer.toString(wColumnConfig.getWidth())+ "px"); //synchrinize header width
		} else {
			//determine the best fit ;-)
			wCell.setTitle(wColumnConfig.getToolTip());
			if (wValueSizeInPixels <= wHeaderSizeInPixels) {
				wCell.setWidth(Integer.toString(wHeaderSizeInPixels)+ "px");
//				wCell.setHeight("100px");
				if (wHeader.getOffsetWidth() < wHeaderSizeInPixels )
					wHeader.setWidth(Integer.toString(wHeaderSizeInPixels)+ "px"); //synchrinize header width
			} else {
				if (wHeader.getOffsetWidth() < wHeaderSizeInPixels )
				wCell.setWidth(Integer.toString(wValueSizeInPixels)+ "px");
//				wCell.setHeight("100px");
				if (wHeader.getOffsetWidth() < wValueSizeInPixels )
					wHeader.setWidth(Integer.toString(wValueSizeInPixels)+ "px"); //synchrinize header width
			}
		}
	}	
	
	
	public void setSize(String width, String height){
//		panel.setWidth(width);
		panel.setHeight(height);
//		headerGrid.setWidth(width);
		
	}

	private void addTextBoxCell(int rowIdx, Gridable wGridableDTO, int colIdx) {
		ColumnConfigIf wColumnConfig = columnConfigs[colIdx];
		String wValue = (String)wGridableDTO.getColumnData(colIdx);
		MobiTextBox wCell = new MobiTextBox(wValue, rowIdx, wGridableDTO, grid);
		wCell.addChangeListener( wColumnConfig.getChangeListener());
		setSize(wColumnConfig, (GridWidget)wCell, wValue, colIdx);
		grid.setWidget(rowIdx, colIdx, wCell);
	}
	
	private void addRichTextAreaCell(int rowIdx, Gridable wGridableDTO, int colIdx) {
		ColumnConfigIf wColumnConfig = columnConfigs[colIdx];
		String wValue = (String)wGridableDTO.getColumnData(colIdx);
		MobiRichTextArea wCell = new MobiRichTextArea(wValue, rowIdx, wGridableDTO, grid);
		setSize(wColumnConfig, (GridWidget)wCell, wValue,colIdx);
		grid.setWidget(rowIdx, colIdx, wCell);
	}
	
	private void addButtonCell(int rowIdx, Gridable wGridableDTO, int colIdx) {
		ColumnConfigIf wColumnConfig = columnConfigs[colIdx];
		String wValue = (String) wGridableDTO.getColumnData(colIdx);
		MobiButton wCell = new MobiButton(wValue, rowIdx, wGridableDTO, grid);
		wCell.addClickListener(wColumnConfig.getClickListener());
		setSize(wColumnConfig, (GridWidget)wCell, wValue,colIdx);
		grid.setWidget(rowIdx, colIdx, wCell);
	}	

	private void addLabelCell(int rowIdx, Gridable wGridableDTO, int colIdx) {
		ColumnConfigIf wColumnConfig = columnConfigs[colIdx];
		String wValue = (String) wGridableDTO.getColumnData(colIdx);
		MobiLabel wCell = new MobiLabel(wValue, true, rowIdx, wGridableDTO, grid);
		setSize(wColumnConfig, (GridWidget)wCell, wValue,colIdx);
		grid.setWidget(rowIdx, colIdx, wCell);
	}

	public int getSelectedIdx() {
		return selectedIdx;
	}

	public Gridable getSelectedObject() {
		return (Gridable) gridables.get(getSelectedIdx());
	}

	public void setSelectedIdx(int selectedIdx) {
		this.selectedIdx = selectedIdx;
	}

	public List getObjects() {
		return gridables;
	}

	public void addObjects(List pGridables) {

		if (!pGridables.isEmpty()) {
			this.columnConfigs = ((Gridable) pGridables.get(0))
					.getColumnConfig();
			this.gridables.addAll(pGridables);
			buildHeader();
			refreshData();
		}
	}

	public void setObjects(List pGridables) {

		if (!pGridables.isEmpty()) {
			this.columnConfigs = ((Gridable) pGridables.get(0))
					.getColumnConfig();
			this.gridables = pGridables;
			buildHeader();
			refreshData();
		}
	}

	public void clear() {
		gridables.clear();
		grid.clear();
	}

	private void addListBoxCell(int rowIdx, Gridable wGridableDTO, int colIdx) {
		ColumnConfigIf wColumnConfig = columnConfigs[colIdx];
		List<String> wValues = (List<String>)wGridableDTO.getColumnData(colIdx);
		MobiListBox wCell = new MobiListBox(false, rowIdx, wGridableDTO, grid);
		for (String string : wValues) {
			wCell.addItem( string );
		}
		wCell.addChangeListener( wColumnConfig.getChangeListener());
		setSize(wColumnConfig, (GridWidget)wCell, "", colIdx);
		grid.setWidget(rowIdx, colIdx, wCell);
	}

}
